#ifndef __m_match_h__
#define __m_match_h__
#include <list>
#include "logerr.hpp"
#include "db.hpp"
#include "room.hpp"
#include "util.hpp"
#include "onlineuser.hpp"
#include <condition_variable>
template <typename T>

/* 简单的阻塞队列*/
class match_queue
{
private:
    std::list<T> _cotain;
    std::mutex _mutex;
    std::condition_variable _cond; /*条件变量*/
public:
    int size()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _cotain.size();
    }
    bool empty()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _cotain.empty();
    }
    void wait()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock); /*释放锁 去改条件变量进行等待*/
    }
    // 入队 生产者调用  每次唤醒所有的线程
    void push(const T &data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cotain.push_back(data);
        _cond.notify_all(); /*唤醒全部的线程*/
    }
    // 出队 消费者 消费数据 当生产的不够某种条件 消费者去等待
    bool pop(T &data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if (_cotain.empty() == true)
        {
            return false;
        }
        data = _cotain.front();
        _cotain.pop_front();
        return true;
    }
    void remove(T &data) /*删除值删除 不是迭代器删除*/
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cotain.remove(data);
    }
};

/* 匹配的逻辑*/
class matcher
{
    /* bronze silver gold*/
private:
    // 队列中存储我们的用户uid
    // 这里的容器是我们自己自己实现的 是线程安全的
    match_queue<uint64_t> _bronze;
    match_queue<uint64_t> _silver;
    match_queue<uint64_t> _gold;

    // 三个线程负责各自的队列
    std::thread _pthread1; // 负责青铜队列
    std::thread _pthread2; // 负责白银
    std::thread _pthread3; // 负责黄金

    user_table *_user;   // 用户信息句柄
    online *_online;     // 连接句柄
    room_manager *_room; // 匹配成功后创建的房间句柄

    void handle_match(match_queue<uint64_t> &mq)
    {
        // 处理匹配逻辑
        while (1)
        {
            // 这里不要用if 防止虚假唤醒 每次唤醒都要满足条件 不满住接着汽去休眠
            while (mq.size() < 2)
            {
                // 阻塞
                mq.wait();
            }
            // 这里要判断此时出队的是不是还在线
            uint64_t uid1, uid2;
            bool ret = mq.pop(uid1);
            if (ret == false)
            {
                continue;
            }
            ret = mq.pop(uid2);
            if (ret == false)
            {
                // 此时不要忘了uid1此时已经被消费了处于匹配中 还应该在匹配队列中
                this->add(uid1);
                continue;
            }
            // 处理掉线情况
            webserve_t::connection_ptr conn1 = _online->get_conn_from_hall(uid1);
            if (conn1.get() == nullptr)
            {
                // 此时用户1掉线并且已经被消费了
                // 此时用户二已经被消费了 但是不确定是不是掉线了
                // 因此我们把用户2加入它对应的队列接着匹配
                this->add(uid2);
                continue;
            }

            webserve_t::connection_ptr conn2 = _online->get_conn_from_hall(uid2);
            if (conn2.get() == nullptr)
            {
                // 此时用户1在线并且已经被消费了
                // 此时用户二已经被消费了 如果掉线了 我们把uid1加入队列再次匹配
                this->add(uid1);
                continue;
            }

            // 这里说明可以为两者创建房间了
            room_ptr rm = _room->create_room(uid1, uid2);
            if (rm.get() == nullptr)
            {
                // 创建房间失败 此是是匹配已经成功 在创建房间的时候失败了 因此
                // 把我们的用户 重新进行匹配
                this->add(uid1);
                this->add(uid2);
                continue;
            }
            // 此时把 匹配完成的结果 房间已经建好的信息 返回给我们的客户端 此时进行的是长连接通信
            Json::Value resp;
            resp["optype"] = "match_success";
            resp["result"] = true;
            std::string message;
            json_util::serialize(resp, message);
            conn1->send(message);
            conn2->send(message);

        }
    }
    void th_normal_entry() { return handle_match(_bronze); }
    void th_high_entry() { return handle_match(_silver); }
    void th_super_entry() { return handle_match(_gold); }

public:
    matcher(user_table *user, online *ol, room_manager *room)
        : _user(user),
          _online(ol),
          _room(room),
          _pthread1(std::thread(&matcher::th_normal_entry, this)),
          _pthread2(std::thread(&matcher::th_high_entry, this)),
          _pthread3(std::thread(&matcher::th_super_entry, this))
    {
        // 初始化我们线程的入口函数
        DBG_LOG("游戏匹配模块初始化完毕....");
    }
    ~matcher()
    {
    }

    // 匹配成功就要进行我们的创建房间把用户加入房间
    // 开始匹配就是把用户放入对应的队列中
    bool add(uint64_t uid)
    {
        // 首先获取我们的用户信息
        Json::Value userinfo;
        bool ret = _user->select_by_id(uid, userinfo);
        if (ret == false)
        {
            DBG_LOG("获取玩家:%s 信息失败！！", std::to_string(uid).c_str());
            return false;
        }
        int score = userinfo["score"].asInt();
        if (score < 2000)
        {
            _bronze.push(uid);
        }
        else if (score >= 2000 && score < 3000)
        {
            _silver.push(uid);
        }
        else
        {
            _gold.push(uid);
        }
        return true;
    }
    bool del(uint64_t uid)
    {
        Json::Value user;
        bool ret = _user->select_by_id(uid, user);
        if (ret == false)
        {
            DBG_LOG("获取玩家:%s 信息失败！！", std::to_string(uid).c_str());
            return false;
        }
        int score = user["score"].asInt();
        // 2. 从队列删除 相当于取消匹配
        if (score < 2000)
        {
            _bronze.remove(uid);
        }
        else if (score >= 2000 && score < 3000)
        {
            _silver.remove(uid);
        }
        else
        {
            _gold.remove(uid);
        }
        return true;
    }
};
#endif


